package com.luych.userModule;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.luych.common.constant.UserMessageConstant;
import com.luych.common.util.ServletUtils;
import com.luych.commonModule.security.SecurityController;
import com.luych.userModule.entity.PermissionInfo;
import com.luych.userModule.entity.UserInfo;
import com.luych.userModule.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.List;

@SpringCloudApplication
@EnableWebSecurity
@EnableRedisHttpSession
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class UserModuleApplication {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public static void main(String[] args) {
        SpringApplication.run(UserModuleApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Bean
    public SecurityController securityController() {
        return new SecurityController();
    }

    @Bean
    public AuthenticationEntryPoint loginUrlAuthenticationEntryPoint() {
        LoginUrlAuthenticationEntryPoint AuthenticationEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
        AuthenticationEntryPoint.setUseForward(true);
        return AuthenticationEntryPoint;
    }

    @Bean
    public RequestCache requestCache() {
        return new HttpSessionRequestCache();
    }

    @Bean
    public ExceptionTranslationFilter exceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, RequestCache requestCache) {
        return new ExceptionTranslationFilter(authenticationEntryPoint, requestCache);
    }

    @Bean
    public UserDetailsService userDetailsService(UserService userService) {
        return loginName -> {
            UserInfo user = userService.getUserByLoginName(loginName);
            if (user == null) {
                logger.debug("user:" + loginName + " not found");
                throw new UsernameNotFoundException(UserMessageConstant.error_UsernameOrPassword);
            }
            User.UserBuilder userBuilder = User.withUsername(user.getLoginName()).password(user.getPassWord());
            List<PermissionInfo> permissions;
            try {
                permissions = userService.getPermissionByUser(user.getUserId());
            } catch (IOException e) {
                logger.debug("permissions get error", e);
                throw new UsernameNotFoundException(UserMessageConstant.error_UsernameOrPassword);
            }
            if (permissions != null && permissions.size() > 0) {
                String[] permissionNames = new String[permissions.size()];
                for (int i = 0; i < permissions.size(); i++) {
                    permissionNames[i] = permissions.get(i).getPermissionName();
                }
                userBuilder.authorities(permissionNames);
            }
            return userBuilder.build();
        };
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return (request, response, authentication) -> {
            logger.debug("user:" + authentication.getPrincipal() + "login success");
            ServletUtils.httpResponseOK(response, UserMessageConstant.message_LoginSuccess);
        };
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return (request, response, exception) -> {
            logger.debug("user login failed", exception);
            ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), UserMessageConstant.error_UsernameOrPassword);
        };
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return (request, response, authentication) -> {
            logger.debug("user:" + authentication.getPrincipal() + "logout success");
            ServletUtils.httpResponseOK(response, UserMessageConstant.message_LogoutSuccess);
        };
    }

    @Bean
    public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter(
            AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler,
            LogoutSuccessHandler logoutSuccessHandler) {
        return new WebSecurityConfigurerAdapter() {
            @Override
            public void configure(HttpSecurity httpSecurity) throws Exception {
                httpSecurity.formLogin().successHandler(successHandler).failureHandler(failureHandler).
                        and().logout().logoutSuccessHandler(logoutSuccessHandler).
                        and().csrf().disable();
            }
        };
    }
}
